package com.cscj.android.repository.network

import com.cscj.android.repository.network.api.ApiResponse
import com.cscj.android.utils.LOG
import com.google.gson.Gson
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.channelFlow
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.koin.core.component.KoinComponent
import org.koin.core.component.inject
import retrofit2.HttpException
import java.net.HttpURLConnection


class ResultFlowTransformer : KoinComponent {

    private val gson by inject<Gson>()
    private val appScope by inject<CoroutineScope>()

    suspend fun <T, R> asResult(
        networkCall: suspend () -> ApiResponse<T>,
        successCall: suspend (ApiResponse<T>) -> Unit = {},
        transformer: (T) -> R,
        dispatcher: CoroutineDispatcher = Dispatchers.IO,
    ): Result<R> {
        return withContext(dispatcher) {
            try {
                val response = networkCall()
                if (response.isSuccessful()) {
                    appScope.launch {
                        successCall(response)
                    }
                    Result.Success(transformer(response.data))
                } else {
                    Result.Error(BusinessException(response.code, response.msg))
                }
            } catch (e: Throwable) {
                handleException(e)
            }
        }
    }

    fun <T, R> asResultFlow(
        networkCall: suspend () -> ApiResponse<T>,
        successCall: suspend (ApiResponse<T>) -> Unit = {},
        transform: (T) -> R,
        dispatcher: CoroutineDispatcher = Dispatchers.IO,
    ): Flow<Result<R>> = flow {
        emit(Result.Loading())
        val response = networkCall()
        if (response.isSuccessful()) {
            appScope.launch {
                successCall(response)
            }
            emit(Result.Success(transform(response.data)))
        } else {
            emit(Result.Error(BusinessException(response.code, response.msg)))
        }
    }.flowOn(dispatcher).catch {
        emit(handleException(it))
    }

    /**
     * networkCall ： 使用retrofit请求网络数据
     * successCall : 网络请求成功之后的操作
     * dispatcher ： 在哪个dispatcher执行
     */
    fun <T> asResultFlow(
        networkCall: suspend () -> ApiResponse<T>,
        successCall: suspend (ApiResponse<T>) -> Unit = {},
        dispatcher: CoroutineDispatcher = Dispatchers.IO,
    ): Flow<Result<T>> = flow {
        emit(Result.Loading())
        val response = networkCall()
        if (response.isSuccessful()) {
            appScope.launch {
                successCall(response)
            }
            emit(Result.Success(response.data))
        } else {
            emit(Result.Error(BusinessException(response.code, response.msg)))
        }
    }.flowOn(dispatcher).catch {
        emit(handleException(it))
    }

    /**
     * 错误处理
     */
    private suspend fun handleException(e: Throwable): Result.Error {
        LOG.e("Network", e)
        if (e is HttpException) {
            if (e.code() == HttpURLConnection.HTTP_FORBIDDEN) {//登出
                return Result.Error(BusinessException(Result.Error.ERROR_BUSINESS, e.message))
            } else {
                val errorBody = e.response()?.errorBody()
                if (errorBody != null) {
                    val errorResponse =
                        gson.fromJson(errorBody.charStream(), ApiResponse::class.java)
                    return if (errorResponse != null) {
                        Result.Error(
                            BusinessException(
                                errorResponse.code, errorResponse.msg
                            )
                        )

                    } else {
                        Result.Error(
                            BusinessException(
                                Result.Error.ERROR_BUSINESS, e.message
                            )
                        )

                    }
                } else {
                    return Result.Error(BusinessException(Result.Error.ERROR_BUSINESS, e.message))
                }
            }

        } else {
            return Result.Error(BusinessException(Result.Error.ERROR_BUSINESS, e.message))
        }
    }

    /**
     * T : 暴露出去的model
     * A ：API返回的model
     */
    fun <T, A> asBoundedResultFlow(
        cacheFlow: () -> Flow<T>,
        networkCall: suspend () -> ApiResponse<A>,
        successCall: suspend (ApiResponse<A>) -> Unit = {},
        shouldRequest: suspend () -> Boolean = { true },
        dispatcher: CoroutineDispatcher = Dispatchers.IO,
    ): Flow<Result<T>> = channelFlow {

        send(Result.Loading(cacheFlow().firstOrNull()))

        if (shouldRequest()) {
            val response = networkCall()

            if (response.isSuccessful()) {
                appScope.launch {
                    successCall(response)
                }
                cacheFlow().collectLatest {
                    send(Result.Success(it))
                }
            } else {
                send(Result.Error(BusinessException(response.code, response.msg)))
            }

        } else {
            cacheFlow().collectLatest {
                send(Result.Success(it))
            }
        }


    }.flowOn(dispatcher).catch {
        emit(handleException(it))
    }

}